#ifndef _CRT_SOCK_H_
#define _CRT_SOCK_H_

#include <crt_core.hpp>

#ifdef __cplusplus
extern "C" {
#endif

#if (TARGET_OS == OS_WIN)

#elif (TARGET_OS == OS_POSIX)
#include <sys/un.h>
#elif (TARGET_OS == OS_MACH)
#include <sys/un.h>
#elif (TARGET_OS == OS_UNIX)
#include <sys/un.h>

#endif

#ifndef NET_PORT
typedef unsigned short NET_PORT;
#endif

#ifndef INVALID_NET_PORT
#define INVALID_NET_PORT 0
#endif

#ifndef NET_ADDR
typedef const char* NET_ADDR;
#endif

#ifndef NET_ADDR_INT
typedef unsigned int NET_ADDR_INT;
#endif

#ifndef NET_ADDR_PORT
typedef unsigned short NET_ADDR_PORT;
#endif

/*
Htons converts unsigned short types from host order to network order
Htonl converts unsigned long type from host order to network order
NTOHS converts unsigned short types from network order to host order
NTOHL converts unsigned long types from network order to host order
*/

struct s_sockaddr_storage {
    union {
        struct sockaddr ss_sa;
        struct sockaddr_in ss_sin;
        struct sockaddr_in6 ss_sin6;
        char ss_padding[128];
    } ss_union;
};

#ifndef SOCKADDR_PTR
typedef const struct sockaddr* SOCKADDR_PTR;
#endif

#if (TARGET_OS == OS_WIN)

typedef SOCKET _sock_t;

#ifndef socklen_t
typedef int socklen_t;
#endif

typedef DWORD sock_size;

#define SHUT_SOCK_RD   SD_RECEIVE
#define SHUT_SOCK_RW   SD_SEND
#define SHUT_SOCK_BOTN SD_BOTH

#define SC_ETIMEDOUT    WSAETIMEDOUT
#define SC_ENOMEM       WSAENOBUFS
#define SC_EINVAL       WSAEINVAL
#define SC_ECONNREFUSED WSAECONNREFUSED
#define SC_ECONNRESET   WSAECONNRESET
#define SC_EHOSTDOWN    WSAEHOSTDOWN
#define SC_EHOSTUNREACH WSAEHOSTUNREACH
#define SC_EINTR        WSAEINTR
#define SC_ENETDOWN     WSAENETDOWN
#define SC_ENETUNREACH  WSAENETUNREACH
#define SC_ENOTCONN     WSAENOTCONN
#define SC_EISCONN      WSAEISCONN
#define SC_EWOULDBLOCK  WSAEWOULDBLOCK
#define SC_EAGAIN       SC_EWOULDBLOCK /*zzz*/
#define SC_ENOBUFS      WSAENOBUFS
#define SC_ECONNABORTED WSAECONNABORTED
#define SC_EINPROGRESS  WSAEINPROGRESS

#ifndef _AFUNIX_
struct sockaddr_un {
    ADDRESS_FAMILY sun_family;
    char sun_path[108];
};
#endif // !_AFUNIX_

#define _ERR_CONNECT_RETRIABLE(e) ((e) == SC_EWOULDBLOCK || (e) == SC_EINTR || (e) == SC_EINPROGRESS || (e) == SC_EINVAL)

#define _ERR_CONNECT_REFUSED(e) ((e) == WSAECONNREFUSED)

#define _ERR_CONNECT_ERRORRESET(e)  ((e) == SC_ECONNRESET)
#define _ERR_CONNECT_ERROR_ENET(e)  ((e) == SC_ENETDOWN || (e) == SC_ENETUNREACH)
#define _ERR_CONNECT_ERROR_EHOST(e) ((e) == SC_EHOSTDOWN || (e) == SC_EHOSTUNREACH)

int _socket_geterror(_sock_t sock);

#elif (TARGET_OS == OS_POSIX)

#define INVALID_SOCKET -1
typedef int _sock_t;
typedef int sock_size;
#define SHUT_SOCK_RD   SHUT_RD
#define SHUT_SOCK_RW   SHUT_WR
#define SHUT_SOCK_BOTN SHUT_RDWR

#define SC_ETIMEDOUT    ETIMEDOUT
#define SC_ENOMEM       ENOMEM
#define SC_EINVAL       EINVAL
#define SC_ECONNREFUSED ECONNREFUSED
#define SC_ECONNRESET   ECONNRESET
#define SC_EHOSTDOWN    EHOSTDOWN
#define SC_EHOSTUNREACH EHOSTUNREACH
#define SC_EINTR        EINTR
#define SC_EAGAIN       EAGAIN
#define SC_ENETDOWN     ENETDOWN
#define SC_ENETUNREACH  ENETUNREACH
#define SC_ENOTCONN     ENOTCONN
#define SC_EISCONN      EISCONN
#define SC_EWOULDBLOCK  EWOULDBLOCK
#define SC_ENOBUFS      ENOBUFS
#define SC_ECONNABORTED ECONNABORTED
#define SC_EINPROGRESS  EINPROGRESS

#define _ERR_CONNECT_RETRIABLE(e) ((e) == SC_EINTR || (e) == SC_EINPROGRESS)
#define _ERR_CONNECT_REFUSED(e)   ((e) == SC_ECONNREFUSED || (e) == SC_EAGAIN)

#define _ERR_CONNECT_ERRORRESET(e)  ((e) == SC_ECONNRESET)
#define _ERR_CONNECT_ERROR_ENET(e)  ((e) == SC_ENETDOWN || (e) == SC_ENETUNREACH)
#define _ERR_CONNECT_ERROR_EHOST(e) ((e) == SC_EHOSTDOWN || (e) == SC_EHOSTUNREACH)

#define _socket_geterror(sock) (errno)

#elif (TARGET_OS == OS_MACH)

#define INVALID_SOCKET -1
typedef int _sock_t;
typedef int sock_size;
#define SHUT_SOCK_RD   SHUT_RD
#define SHUT_SOCK_RW   SHUT_WR
#define SHUT_SOCK_BOTN SHUT_RDWR

#define SC_ETIMEDOUT    ETIMEDOUT
#define SC_ENOMEM       ENOMEM
#define SC_EINVAL       EINVAL
#define SC_ECONNREFUSED ECONNREFUSED
#define SC_ECONNRESET   ECONNRESET
#define SC_EHOSTDOWN    EHOSTDOWN
#define SC_EHOSTUNREACH EHOSTUNREACH
#define SC_EINTR        EINTR
#define SC_EAGAIN       EAGAIN
#define SC_ENETDOWN     ENETDOWN
#define SC_ENETUNREACH  ENETUNREACH
#define SC_ENOTCONN     ENOTCONN
#define SC_EISCONN      EISCONN
#define SC_EWOULDBLOCK  EWOULDBLOCK
#define SC_ENOBUFS      ENOBUFS
#define SC_ECONNABORTED ECONNABORTED
#define SC_EINPROGRESS  EINPROGRESS

#define _ERR_CONNECT_RETRIABLE(e) ((e) == SC_EINTR || (e) == SC_EINPROGRESS)
#define _ERR_CONNECT_REFUSED(e)   ((e) == SC_ECONNREFUSED || (e) == SC_EAGAIN)

#define _ERR_CONNECT_ERRORRESET(e)  ((e) == SC_ECONNRESET)
#define _ERR_CONNECT_ERROR_ENET(e)  ((e) == SC_ENETDOWN || (e) == SC_ENETUNREACH)
#define _ERR_CONNECT_ERROR_EHOST(e) ((e) == SC_EHOSTDOWN || (e) == SC_EHOSTUNREACH)

#define _socket_geterror(sock) (errno)

#elif (TARGET_OS == OS_UNIX)

#define INVALID_SOCKET -1
typedef int _sock_t;
typedef int sock_size;
#define SHUT_SOCK_RD   SHUT_RD
#define SHUT_SOCK_RW   SHUT_WR
#define SHUT_SOCK_BOTN SHUT_RDWR

#define SC_ETIMEDOUT    ETIMEDOUT
#define SC_ENOMEM       ENOMEM
#define SC_EINVAL       EINVAL
#define SC_ECONNREFUSED ECONNREFUSED
#define SC_ECONNRESET   ECONNRESET
#define SC_EHOSTDOWN    EHOSTDOWN
#define SC_EHOSTUNREACH EHOSTUNREACH
#define SC_EINTR        EINTR
#define SC_EAGAIN       EAGAIN
#define SC_ENETDOWN     ENETDOWN
#define SC_ENETUNREACH  ENETUNREACH
#define SC_ENOTCONN     ENOTCONN
#define SC_EISCONN      EISCONN
#define SC_EWOULDBLOCK  EWOULDBLOCK
#define SC_ENOBUFS      ENOBUFS
#define SC_ECONNABORTED ECONNABORTED
#define SC_EINPROGRESS  EINPROGRESS

#define _ERR_CONNECT_RETRIABLE(e) ((e) == SC_EINTR || (e) == SC_EINPROGRESS)
#define _ERR_CONNECT_REFUSED(e)   ((e) == SC_ECONNREFUSED || (e) == SC_EAGAIN)

#define _ERR_CONNECT_ERRORRESET(e)  ((e) == SC_ECONNRESET)
#define _ERR_CONNECT_ERROR_ENET(e)  ((e) == SC_ENETDOWN || (e) == SC_ENETUNREACH)
#define _ERR_CONNECT_ERROR_EHOST(e) ((e) == SC_EHOSTDOWN || (e) == SC_EHOSTUNREACH)

#define _socket_geterror(sock) (errno)

#endif

typedef union {
    struct sockaddr_storage ss;
    struct sockaddr_in6 in6;
    struct sockaddr_in in;

#if (TARGET_OS != OS_WIN)
    struct sockaddr_un un;
#endif

    struct sockaddr sa;
} sock_addr;

int _bind_unixsock(_sock_t s, const char* name, size_t len, int af, int scount);
int _bind_stcpsockv4(_sock_t s, const char* name, size_t len, int scount, unsigned short port);
int _bind_stcpsockv6(_sock_t s, const char* name, size_t len, int scount, unsigned short port);

int _connect_unixsock(_sock_t s, const char* name, size_t len, int af);
int _connect_ctcpsockv4(_sock_t s, const char* name, size_t len, unsigned short port);
int _connect_ctcpsockv6(_sock_t s, const char* name, size_t len, unsigned short port);

int _asynconnect_sockv4(_sock_t s, const char* name, size_t len, unsigned short port);
int _asynconnect_sock(_sock_t s, SOCKADDR_PTR sa, socklen_t socklen);

int _connected_socket(_sock_t s);
int socket_cloexec(_sock_t s);

_sock_t _createsock(int af, int type, int protocol);

int _closesock(_sock_t s);
int _stable__closesock(_sock_t s);

int _shutdown_sock(_sock_t s, int howrw);
int is_listen_socket(_sock_t s);
int getsocktype(_sock_t s);

int set_blocking(_sock_t s, int turn);
int set_sockerror(_sock_t s, int turn);
int set_reuseaddr(_sock_t s, int turn);
int set_nodelay(_sock_t s, int turn);
int set_deferaccpet(_sock_t s, int turn);
int set_broadcast(_sock_t s, int turn);
int set_dontroute(_sock_t s, int turn);
int set_keepalive(_sock_t s, int turn);
int set_cork(_sock_t s, int turn);
int set_nosigpipe(_sock_t s, int turn);
int set_dolinger(_sock_t s, int turn);
int set_reuseable(_sock_t s, int turn);
int set_reuseableport(_sock_t s, int turn);
int set_linger(_sock_t s, int sec);
int set_sendtime(_sock_t s, int sec);
int set_recvtime(_sock_t s, int sec);
int set_connectime(_sock_t s, int sec);
int set_sendbuflen(_sock_t s, socklen_t size);
int set_recvbuflen(_sock_t s, socklen_t size);
int set_sendlowat(_sock_t s, socklen_t size);
int set_recvlowat(_sock_t s, socklen_t size);

#if (TARGET_OS == OS_WIN)
int socketpair(int d, int type, int protocol, _sock_t sv[2]);
#endif

#ifdef __cplusplus
}
#endif

#endif
